/*
 * graph.c
 *
 *  Created on: Aug 6, 2013
 *      Author: cdoar
 */

#include "graph.h"

vertex * graph;
int arrSize = 1;
int num_of_vertices = 0;
int fatal_error = 0; /*error constructing the graph*/

int generate(FILE *network) {
	int num_commands;
	char command_name[num_char + 1], param1[num_char + 1], param2[num_char + 1],
			param3[num_char + 1];
	char command_line[num_char + 2]; /* Command line string includes the new line character ('\n') and a null character ('\0')  */
	while (!feof(network)) {
		if (fgets(command_line, sizeof(command_line), network) == NULL
				&& !feof(network)) { /*This function reads up to sizeof(fullcommand)-1 characters, or until it encounters the new line character. The function adds '\0' to the end of the array */
			perror("Error: standard function fgets has failed");
			freeGraph();/* error case */
			break; /* breaks the while(1) loop */
		}
		if (command_line[sizeof(command_line) - 2] != '\n'
				&& strlen(command_line) == num_char + 1) { /*# of characters (excluding '\n') in command_line is <=num_char iff the condition takes place. */
			printf(
					"Error: command length must be less than or equal to %d characters\n",
					num_char);
			while (getchar() != '\n') {
			} /* read the remaining characters */
			continue;/* continue with  while(1) loop */
		}
		if (only_whitespaces(command_line)) { /* Checks that command-line is composed only from whitespace characters */
			printf(
					"Error: line command must have at least one non-whitespace character\n");
			continue; /* continue with  while(1) loop */
		}
		num_commands = getCommand(command_line, command_name, param1, param2,
				param3);
		if (num_commands > 0) {/*if getCommand was successful*/
			executeCommand(command_name, param1, param2, param3, num_commands);
		}
		if (fatal_error) {
			return 0;
		}
	}
	return 1;
}

/*
 * getCommand
 *
 * scan using sscanf user input from command_line in to separate  strings,
 * if there is at least one input scanned return number of sscanf successes,
 * if sscanf returned EOF or zero assume sscanf failed.
 * initiate call print error call quit() return 0 this will cause the program to terminate.
 */
int getCommand(char *command_line, char *command_name, char *param1,
		char *param2, char *param3) {
	int scanned;

	scanned = sscanf(command_line, "%s %s %s %s", command_name, param1, param2,
			param3);
	if (scanned == EOF || scanned == 0) {
		perror("Error: standard function sscanf has failed");
		freeGraph();
		return 0;
	}

	return scanned;
}
/*
 * executeCommand
 *
 * check if the command scanned is found,if the command is found:
 * check that appropriate number of arguments was scanned in getCommands using validateInput() function.
 * (we are assuming that the user input is correct).
 * if the number of arguments is correct call the command function,
 * else it is assumed that sscanf failed and validateInput() print error call quit and terminate.
 * if the command is not found: prints error and continues.
 */
void executeCommand(char *command_name, char *param1, char *param2,
		char *param3, int num_args) {

	if (isType(command_name, ADD_VERTEX)) {
		if (validateInput(num_args, 2, ADD_VERTEX)) {
			addVertex(param1);
		}
	} else if (isType(command_name, ADD_EDGE)) {
		if (validateInput(num_args, 4, ADD_EDGE)) {
			if (validWeight(param3)) {
				addEdge(param1, param2, param3);
			} else {
				printf("Error:wrong weight string format\n");
			}
		}
	} else {
		printf("Error: command not found\n");
	}
}

int validWeight(char *str) {

	int dot = 0;
	int i = 0;
	while (str[i]) {
		if (str[i] == '.') {
			if(!dot && i==0){
				return 0;
			}
			else if (!dot) {
				dot = 1;
			} else {
				return 0;
			}
		}

		if(!isdigit(str[i]) && str[i]!='.'){
			return 0;
		}


		i++;
	}
	return 1;
}

/*
 * addVertex,
 *
 * add vertex to vertexArr, initialize the proper fields.
 */
void addVertex(char *name) {
	vertex *realloced_arr;

	if (!num_of_vertices) {
		graph = (vertex*) malloc(sizeof(vertex));
		CHECK_ALLOC(graph, MALLOC_ERROR);
	}

	if (!uniqueName(name)) {
		printf("Error: vertex with name:%s already exist", name);
	}

	if (num_of_vertices == arrSize) {
		realloced_arr = (vertex*) realloc(graph,
				sizeof(vertex) * (arrSize * 2));
		CHECK_ALLOC(realloced_arr, REALLOC_ERROR);
		graph = realloced_arr;
		arrSize *= 2;
	}

	graph[num_of_vertices] = (vertex) malloc(sizeof(struct vertex_s));
	CHECK_ALLOC(graph[num_of_vertices], MALLOC_ERROR);

	graph[num_of_vertices]->ID = num_of_vertices;
	graph[num_of_vertices]->cluster = -1;
	strcpy(graph[num_of_vertices]->name, name);

	graph[num_of_vertices]->edges = (linked_list) malloc(
			sizeof(struct linked_list_s));
	CHECK_ALLOC_VTX(graph[num_of_vertices]->edges, MALLOC_ERROR,
			graph[num_of_vertices], ALLOC_ERR);
	graph[num_of_vertices]->edges->head = NULL;
	num_of_vertices++;
}

/*
 * uniqueName
 *
 * checks if vertex with the same name already exist in the graph.
 */
int uniqueName(char *name) {
	int i;
	for (i = 0; i < num_of_vertices; ++i) {
		if (!strcmp(name, graph[i]->name)) {
			return 0;
		}
	}
	return 1;
}

/*
 * addEdge
 *
 * add second vertex id to first vertex edges list as the first element of the list, and the other way.
 */
void addEdge(char *first_vertex, char *second_vertex, char *edge_weight) {
	unsigned id1, id2;
	double weight;
	linked_list edges1, edges2;

	id1 = toUnsigned(first_vertex);
	id2 = toUnsigned(second_vertex);
	weight = toDouble(edge_weight);
	if (!idCheck(id1) || !idCheck(id2)) {
		return;
	}

	if (weight <= 0 || weight > 1) {
		printf("Error: edge weight must be in range (0,1]\n");
		return;
	}
	if (id1 == id2) {
		printf("Error: edge must be between two different vertices\n");
		return;
	}

	edges1 = graph[id1]->edges;
	edges2 = graph[id2]->edges;

	if (contains(edges1, edges2, id2, id1)) {
		printf("Error: edge is already in the graph\n");
		return;
	}

	/*add to edges1 if successful add to edges2 */
	if (add(edges1, id2, weight)) {
		add(edges2, id1, weight);
	}
}

int add(linked_list list, int id, double weight) {
	element temp;
	temp = (element) malloc(sizeof(struct element_s));
	if (temp == NULL ) {
		perror(MALLOC_ERROR);
		freeGraph();
		return 0;
	}
	temp->id_vetex = id;
	temp->weight = weight;

	/*add id to list*/
	if (list->head == NULL ) {/*if list is empty*/
		list->head = temp;
		temp->next = NULL;
	} else {
		temp->next = list->head;
		list->head = temp;
	}
	return 1;
}

/*
 * contains
 *
 * check if the list elements contains id.
 */
int contains(linked_list list1, linked_list list2, unsigned id1, unsigned id2) {
	element iter1, iter2;
	iter1 = list1->head;
	iter2 = list2->head;
	while (iter1 != NULL && iter2 != NULL ) {
		if (iter1->id_vetex == id1 || iter2->id_vetex == id2) {
			return 1;
		}
		iter1 = iter1->next;
		iter2 = iter2->next;
	}
	return 0;
}

/*
 * download
 *
 * downloads the block_num block to the vertex with the vertex_id id,
 * download use the BFS algorithm, upon success prints download route using printer function,
 * if fails prints error message.

 void download(char *vertex_id, char *block_num) {
 unsigned id, blk;
 element last, iter;
 linked_list queue;
 int *parents, *discovered;
 vertex vtx;
 id = toUnsigned(vertex_id);
 blk = toUnsigned(block_num);
 if (!idCheck(id)) {
 return;
 }

 vtx = graph[id];

 parents = (int*) calloc(num_of_vertices, sizeof(int));
 CHECK_ALLOC(parents, CALLOC_ERROR);
 discovered = (int*) calloc(num_of_vertices, sizeof(int));
 CHECK_ALLOC_DOWNLOAD(discovered, CALLOC_ERROR, parents, NOT_ALLOCATED,
 NOT_ALLOCATED);
 queue = (linked_list) malloc(sizeof(struct linked_list_s));
 CHECK_ALLOC_DOWNLOAD(queue, MALLOC_ERROR, parents, discovered,
 NOT_ALLOCATED);
 queue->head = malloc(sizeof(struct element_s));
 if (queue->head == NULL ) {
 perror(MALLOC_ERROR);
 quit();
 return;
 }
 last = queue->tail = queue->head;
 last->next = NULL;
 last->id_vetex = id;
 parents[id] = -1;
 discovered[id] = 1;
 while (last != NULL ) {
 if (isType(graph[last->id_vetex]->type,PEER)
 && graph[last->id_vetex]->blocks[blk]) {
 graph[id]->blocks[blk] = 1;
 printer(last->id_vetex, id, blk, parents);
 break;
 }
 for (iter = graph[last->id_vetex]->edges->head; iter != NULL ; iter =
 iter->next) {
 if (!discovered[iter->id_vetex]) {
 discovered[iter->id_vetex] = 1;
 parents[iter->id_vetex] = last->id_vetex;
 queue->tail->next = malloc(sizeof(struct element_s));
 CHECK_ALLOC_DOWNLOAD(queue->tail->next, MALLOC_ERROR, parents,
 discovered, queue);
 queue->tail = queue->tail->next;
 queue->tail->id_vetex = iter->id_vetex;
 queue->tail->next = NULL;
 }
 }
 queue->head = queue->head->next;
 free(last);
 last = queue->head;
 }
 if (last == NULL ) {
 printf("Error: cannot find a peer with block %u\n", blk);
 }
 free(discovered);
 free(parents);
 freeList(queue);

 }
 */

/*
 * quit
 *
 * free allocated memory,set exit_prog to 0 means the program will exit.
 */
void freeGraph() {
	int i;
	fatal_error = 1;
	for (i = 0; i < num_of_vertices; i++) {
		freeVertex(graph[i], ALLOCATED);
	}
	free(graph);
}

/*
 * quit_download
 *
 * the function frees its arguments and call quit()

 void quit_download(int* arr1, int* arr2, linked_list list) {
 if (arr1 != NULL ) {
 free(arr1);
 }
 if (arr2 != NULL ) {
 free(arr2);
 }
 if (list != NULL ) {
 freeList(list);
 }
 quit();
 } */

/*
 * freeList
 *
 * free the list's elements and finally the list itself.
 */
void freeList(linked_list list) {
	element iter;
	element temp;
	if (list == NULL ) {/*the list doesn't exist*/
		return;
	}
	for (iter = list->head; iter != NULL ; iter = temp) {
		temp = iter->next;
		free(iter);
	}
	free(list);
}

/*
 * only_whitespaces
 *
 * check if the given string contains only white spaces.
 */
int only_whitespaces(char * str) {
	int i = 0;
	while (str[i]) {
		if (!isspace(str[i])) {
			return 0;
		}
		i++;
	}
	return 1;
}

/*
 * toUnsigned
 *
 * convert string to unsigned return the unsigned.
 */
unsigned toUnsigned(char *temp) {
	unsigned result;
	int converted;
	result = 0;
	converted = sscanf(temp, "%u", &result);
	if (converted != 1) {
		perror("Error: standard function sscanf has failed");
		freeGraph();
		return 0;
	}
	return result;
}

double toDouble(char *temp) {
	double result;
	int converted;
	result = 0;
	converted = sscanf(temp, "%lf", &result);
	if (converted != 1) {
		perror("Error: standard function sscanf has failed");
		freeGraph();
		return 0;
	}
	sprintf(temp, "%.3f", result);
	converted = sscanf(temp, "%lf", &result);
	if (converted != 1) {
		perror("Error: standard function sscanf has failed");
		freeGraph();
		return 0;
	}
	return result;
}
/*
 * idCheck
 *
 * check if the id is valid.if true return 1,else print error return 0.
 */
int idCheck(unsigned id) {
	if ((id >= num_of_vertices || id < 0) || (num_of_vertices == 0)) {
		printf("Error: ID number is not in the system\n");
		return 0;
	}
	return 1;
}

/*
 * validateInput
 *
 * check if the number of item scanned is matching the number of arguments expected
 * by assuming user input is correct if the numbers don't match the function prints error
 * and return 0,else return 1.
 */
int validateInput(int scanned, int expected, char *name) {
	if (scanned != expected) {
		printf("Error: function %s excepts %d arguments %d are given", name,
				expected, scanned);
		return 0;
	}
	return 1;
}

/*
 * freeVertex
 *
 * free allocated vertex, if there were errors during the creation of the vertex
 * if !alloc_err the vertex was allocated successfully
 * if alloc_err = 1 the error was while allocating the edges
 *
 */
void freeVertex(vertex vtx, int alloc_err) {
	if (!alloc_err) {/*the vertex was allocated successfully*/
		freeList(vtx->edges);
		free(vtx);
	} else {/*the error was while allocating the edges list*/
		free(vtx);
	}

}

void printGraph() {
	int i;
	element itr;
	for (i = 0; i < num_of_vertices; ++i) {
		printf("name: %s, id: %u\n", graph[i]->name, graph[i]->ID);
		/*for(itr = graph[i]->edges->head;itr!=NULL;itr=itr->next){
		 printf("edge:(%u,%u) weight: %f\n",graph[i]->ID,itr->id_vetex,itr->weight);

		 }*/
	}
	for (i = 0; i < num_of_vertices; ++i) {
		for (itr = graph[i]->edges->head; itr != NULL ; itr = itr->next) {
			if (itr->id_vetex > graph[i]->ID) {
				printf("edge:(%u,%u) weight: %f\n", graph[i]->ID, itr->id_vetex,
						itr->weight);
			}

		}
	}
}

